package com.woniu.utils;
import com.woniu.exception.DIYException;
import lombok.Data;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 校验参数工具
 *
 */
@Data
public class ParamUtil {

    private Boolean go = true;//最终结果

    private ParamUtil setGo(boolean go) {
        this.go = go;
        return this;
    }

    //构建参数校验器
    public static ParamUtil bulid(){
        return new ParamUtil();
    }

    //构建多个对象，校验所有属性
    public static ParamUtil bulids(Object ... objs){
        return new ParamUtil().checkParams(objs);
    }

    //构建一个对象，排除属性校验
    public static ParamUtil bulidOther(Object obj,String ... fieldNames){
        return new ParamUtil().checkOtherParam(obj,fieldNames);
    }

    //构建一个对象，必填属性校验
    public static ParamUtil bulidMust(Object obj,String ... fieldNames){
        return new ParamUtil().checkMustParam(obj,fieldNames);
    }


    /**
     * 多个对象校验器（不附带规则）
     * @param objs
     * @return
     * @throws Exception
     */
    public ParamUtil checkParams(Object ... objs){
        for (Object obj : objs) {
            if (!checks(obj,new HashMap<>(),false,null).getGo()) break;
        }
        return this;
    }

    /**
     * 单个对象校验器（带排除规则）
     * @param obj
     * @param fieldNames
     * @return
     * @throws Exception
     */
    public ParamUtil checkOtherParam(Object obj,String ... fieldNames) {
        HashMap<String, String> map = new HashMap<>();
        for (String name : fieldNames) {
//            System.out.println("需要排除字段："+name);
            map.put(name,null);
        }
        return checks(obj,map,false,null);
    }

    /**
     * 单个对象校验器（带必填规则）
     * @param obj
     * @param fieldNames
     * @return
     * @throws Exception
     */
    public ParamUtil checkMustParam(Object obj,String ... fieldNames) {
        HashMap<String, String> map = new HashMap<>();
        for (String name : fieldNames) {
            map.put(name,null);
        }
        return checks(obj,map,true,null);
    }


    //先判断是否排除 再判断是否为null 再判断类型,如果是对象或集合执行递归

    private ParamUtil checks(Object obj,Map<String,String> map,Boolean bool,String FieldName) {
//        System.out.println("当前验证权限定名："+FieldName);
        //判断是否排除
        if(bool){//必填
            if (FieldName==null||map.containsKey(FieldName));//如果存在就继续执行
            else return this;//不存在就排除
        }else if (FieldName!=null&&map.containsKey(FieldName)) return this;//如果存在就排除
        if (obj==null) throw new DIYException(-9999,"参数异常");//为null就结束
        Class<?> clazz = obj.getClass();
        //判断是否是基本类型
        if (isBasicType(clazz,Date.class)) return this;//走基本类型校验处理器
        else if(clazz==String.class) {
            if (((String) obj).trim().equals(""))  throw new DIYException(-9999,"参数异常");
        }else if(Collection.class.isAssignableFrom(clazz)){//集合类型校验处理器
            Collection collection = (Collection) obj;
            Iterator iterator = collection.iterator();
            if (iterator.hasNext()) checks(iterator.next(),map,bool,FieldName);
        }else if(Map.class.isAssignableFrom(clazz)){//map类型校验处理器
            Map objMap = (Map) obj;
            for (Object o : objMap.keySet()) checks(o,map,bool,FieldName);
        }else {//对象类型校验处理器
            //遍历所有属性对象
            for (Field field : clazz.getDeclaredFields()) {
                if(!this.getGo()) return this;
                //调Getter方法取值
                Method method = null;
                try {
                    method = clazz.getMethod("get" + getMethodName(field.getName()));
                    Object objectValue = method.invoke(obj);
                    String thisName = (FieldName==null?"":FieldName+".")+field.getName();//当前属性的权限定名
                    checks(objectValue,map,bool,thisName);
                } catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
                    e.printStackTrace();
                    throw new DIYException(-9999,"方法未定义或服务器异常");
                }
            }

        }
        return this;
    }

    /**
     * 将首字母变大写（效率是最高）
     * @param name
     * @return
     */
    private String getMethodName(String name){
        byte[] items = name.getBytes();
        items[0] = (byte) ((char) items[0] - 'a' + 'A');
        return new String(items);
    }

    /**
     * 用于判断该类型是否是基本类型(不包括string类型)
     * @param typeClass 待匹配的类型
     * @param classes 用于追加匹配类型
     * @return
     */
    public boolean isBasicType(Class<?> typeClass,Class<?> ... classes){
        if(typeClass==Integer.class||typeClass==Long.class||typeClass==Double.class||
                typeClass==Boolean.class||typeClass==Float.class||typeClass==Byte.class||
                typeClass==Short.class|| typeClass==Character.class){
            return true;
        }else{
            for (Class<?> clazz : classes) {
                if(typeClass==clazz) return true;
            }
            return false;
        }
    }
}
